﻿using Fast;
using Fast.ConfigureOptions;
using System;
using System.Linq;
using System.Reflection;

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// 配置选项服务扩展类
    /// </summary>
    [SkipScan]
    public static class ConfigureOptionsServiceCollectionExtensions
    {
        /// <summary>
        /// 添加选项配置
        /// </summary>
        /// <typeparam name="TOptions">选项类型</typeparam>
        /// <param name="services">服务集合</param>
        /// <param name="configure">配置 Options</param>
        /// <returns>服务集合</returns>
        public static IServiceCollection AddConfigureOptions<TOptions>(this IServiceCollection services, Action<TOptions> configure = null)
            where TOptions : class, IConfigureOptions
        {
            var optionsType = typeof(TOptions);
            var optionsSettings = optionsType.GetCustomAttribute<ConfigureOptionsAttribute>(false);

            // 获取键名
            var jsonKey = GetOptionsJsonKey(optionsSettings, optionsType);

            // 配置选项（含验证信息）
            var configurationRoot = services.GetConfiguration();
            var optionsConfiguration = configurationRoot.GetSection(jsonKey);

            if (configure == null)
            {
                services.Configure<TOptions>(optionsConfiguration);
            }
            else
            {
                services.Configure<TOptions>(options =>
                {
                    configure.Invoke(options);
                });
            }

            var optionsInterface = optionsType.GetInterfaces().FirstOrDefault(t =>
                t.IsGenericType && typeof(IConfigureOptions).IsAssignableFrom(t.GetGenericTypeDefinition()));
            if (optionsInterface != null)
            {
                var genericTypeArguments = optionsInterface.GenericTypeArguments;

                if (genericTypeArguments.Length > 0)
                {
                    services.AddSingleton(genericTypeArguments.Last());
                }

                // 配置后期配置
                var postConfigureMethod = optionsType.GetMethod(nameof(IConfigureOptions<TOptions>.PostConfigure));
                if (postConfigureMethod != null)
                {
                    if (optionsSettings?.PostConfigureAll != true)
                        services.PostConfigure<TOptions>(options =>
                            postConfigureMethod.Invoke(options, new object[] { options, optionsConfiguration }));
                    else
                        services.PostConfigureAll<TOptions>(options =>
                            postConfigureMethod.Invoke(options, new object[] { options, optionsConfiguration }));
                }
            }

            return services;
        }

        /// <summary>
        /// 获取选项键
        /// </summary>
        /// <param name="configureOptions">选项配置特性</param>
        /// <param name="optionsType">选项类型</param>
        /// <returns></returns>
        private static string GetOptionsJsonKey(ConfigureOptionsAttribute configureOptions, Type optionsType)
        {
            // 默认后缀
            var defaultStuffx = nameof(Options);

            return configureOptions switch
            {
                // // 没有贴 [ConfigureOptions]，如果选项类以 `Options` 结尾，则移除，否则返回类名称
                null => optionsType.Name.EndsWith(defaultStuffx)
                    ? optionsType.Name[0..^defaultStuffx.Length]
                    : optionsType.Name,
                // 如果贴有 [ConfigureOptions] 特性，但未指定 JsonKey 参数，则直接返回类名，否则返回 JsonKey
                _ => string.IsNullOrWhiteSpace(configureOptions.JsonKey) ? optionsType.Name : configureOptions.JsonKey,
            };
        }
    }
}